import { hexMD5, strMD5, CryptoJS } from './../thirdparty/index.js'
import { JJUtil, JJLog } from "../util/index.js";

export default class JJCrypt {

    // ECB 模式的 DES加密
    static encryptByDESWIthECB(message, keyStr, ivStr) {
        let param = {
            mode: CryptoJS.mode.ECB,
            padding: CryptoJS.pad.Pkcs7
        };
        if (JJUtil.isNotNull(ivStr) && ivStr.length > 0) {
            const ivHex = CryptoJS.enc.Utf8.parse(ivStr);
            param.iv = ivHex
        }
        const keyHex = CryptoJS.enc.Utf8.parse(keyStr);
        var encrypted = CryptoJS.DES.encrypt(message, keyHex, param);
        return encrypted.toString();
    }

    // ECB 模式的 DES解密
    static decryptByDESWIthECB(ciphertext, keyStr, ivStr) {
        let param = {
            mode: CryptoJS.mode.ECB,
            padding: CryptoJS.pad.Pkcs7
        };
        if (JJUtil.isNotNull(ivStr) && ivStr.length > 0) {
            const ivHex = CryptoJS.enc.Utf8.parse(ivStr);
            param.iv = ivHex
        }
        var keyHex = CryptoJS.enc.Utf8.parse(keyStr);
        var decrypted = CryptoJS.DES.decrypt({
            ciphertext: CryptoJS.enc.Base64.parse(ciphertext)
        }, keyHex, param);
        return decrypted.toString(CryptoJS.enc.Utf8);
    }

    // CBC 模式的 DES加密
    static desEncrypt(msgStr, keyStr, ivStr) {
        var keyHex = CryptoJS.enc.Utf8.parse(keyStr);
        var ivHex = CryptoJS.enc.Utf8.parse(ivStr);

        var encrypted = CryptoJS.DES.encrypt(msgStr, keyHex, {
            iv: ivHex,
            mode: CryptoJS.mode.CBC, // 加密模式CBC
            padding: CryptoJS.pad.Pkcs7 // 填充模式Pkcs7
        }
        )

        var str = encrypted.ciphertext.toString() // base64格式的密文（encrypted.toString() ？？）
        return str
    }

    // CBC 模式的 DES解密
    static desDecrypt(msgStr, keyStr, ivStr) {
        var keyHex = CryptoJS.enc.Utf8.parse(keyStr);
        var ivHex = CryptoJS.enc.Utf8.parse(ivStr);

        var decrypted = CryptoJS.DES.decrypt({
            ciphertext: CryptoJS.enc.Hex.parse(msgStr)
        }, keyHex, {
                iv: ivHex,
                mode: CryptoJS.mode.CBC,
                padding: CryptoJS.pad.Pkcs7
            });

        var str = decrypted.toString(CryptoJS.enc.Utf8)
        return str
    }

    // aes
    static aesEncrypt(msgStr, keyStr, ivStr) {
        if ('object' == typeof (msgStr)) {
            msgStr = JSON.stringify(msgStr)
        }

        if (JJUtil.isNotNull(keyStr) && keyStr.length > 32) {
            keyStr = keyStr.substr(0, 32)
        }

        if (JJUtil.isNotNull(ivStr) && ivStr.length > 16) {
            ivStr = ivStr.substr(0, 16)
        }

        var keyHex = CryptoJS.enc.Utf8.parse(keyStr); // 加密密钥要求是32位长字符串，不足按padding方式补充
        var ivHex = CryptoJS.enc.Utf8.parse(ivStr); // iv初始化向量是16位长度字符串

        var encrypted = CryptoJS.AES.encrypt(msgStr, keyHex, {
            iv: ivHex,
            mode: CryptoJS.mode.CBC,
            padding: CryptoJS.pad.Pkcs7
        }
        )

        var str = encrypted.ciphertext.toString()
        return str
    }

    static aesDecrypt(msgStr, keyStr, ivStr) {
        if (JJUtil.isNotNull(keyStr) && keyStr.length > 32) {
            keyStr = keyStr.substr(0, 32)
        }

        if (JJUtil.isNotNull(ivStr) && ivStr.length > 16) {
            ivStr = ivStr.substr(0, 16)
        }

        var keyHex = CryptoJS.enc.Utf8.parse(keyStr);
        var ivHex = CryptoJS.enc.Utf8.parse(ivStr);

        var decrypted = CryptoJS.AES.decrypt({
            ciphertext: CryptoJS.enc.Hex.parse(msgStr)
        }, keyHex, {
                iv: ivHex,
                mode: CryptoJS.mode.CBC,
                padding: CryptoJS.pad.Pkcs7
            });

        var str = decrypted.toString(CryptoJS.enc.Utf8)
        return str
    }

    // pbeWithMd5
    static pbeWithMd5AndDesEncrypt(msgStr, pwd, salt, iterationCount) {
        const keyAndivHexObj = this.getKeyHexAndIvHexOfPbeWithMd5AndDes(pwd, salt, iterationCount)
        if (JJUtil.isEmpty(keyAndivHexObj)) {
            return null;
        }
        const keyHex = keyAndivHexObj.keyHex
        const ivHex = keyAndivHexObj.ivHex

        var encrypted = CryptoJS.DES.encrypt(msgStr, keyHex, {
            iv: ivHex,
            mode: CryptoJS.mode.CBC,
            padding: CryptoJS.pad.Pkcs7
        }
        )

        var result = encrypted.ciphertext.toString()
        return result

        // test case: pbeWithMd5AndDesEncrypt('123456', '123456', '123456', 50) => B0E6CDB896734198
    }

    static pbeWithMd5AndDesDecrypt(msgHexStr, pwd, salt, iterationCount) {
        const keyAndivHexObj = this.getKeyHexAndIvHexOfPbeWithMd5AndDes(pwd, salt, iterationCount)
        if (JJUtil.isEmpty(keyAndivHexObj)) {
            return null;
        }
        const keyHex = keyAndivHexObj.keyHex
        const ivHex = keyAndivHexObj.ivHex

        var decrypted = CryptoJS.DES.decrypt({
            ciphertext: CryptoJS.enc.Hex.parse(msgHexStr)
        }, keyHex, {
                iv: ivHex,
                mode: CryptoJS.mode.CBC,
                padding: CryptoJS.pad.Pkcs7
            });

        const result = decrypted.toString(CryptoJS.enc.Utf8)
        return result
    }

    static getKeyHexAndIvHexOfPbeWithMd5AndDes(pwd, salt, iterationCount) {
        let str = pwd + salt
        let i = 1
        while (i < iterationCount) {
            str = strMD5(str)
            ++i;
        }

        const key = hexMD5(str) // 最后一次加密返回hex格式
        const iv = key.substring(key.length / 2, key.length)

        const keyHex = CryptoJS.enc.Hex.parse(key)
        const ivHex = CryptoJS.enc.Hex.parse(iv)
        if (JJUtil.isEmpty(keyHex) || JJUtil.isEmpty(ivHex)) {
            JJLog.error('PbeWithMd5AndDes secret key error: keyHex: ' + keyHex + '; ivHex:' + ivHex)
            return null;
        }
        return { keyHex, ivHex }
    }

    static pbeWithMd5AndDesEncrypt2(msgStr, keyAndivHexObj) {
        const keyHex = keyAndivHexObj.keyHex
        const ivHex = keyAndivHexObj.ivHex

        var encrypted = CryptoJS.DES.encrypt(msgStr, keyHex, {
            iv: ivHex,
            mode: CryptoJS.mode.CBC,
            padding: CryptoJS.pad.Pkcs7
        }
        )

        var result = encrypted.ciphertext.toString()
        return result
    }

    static pbeWithMd5AndDesDecrypt2(msgHexStr, keyAndivHexObj) {
        const keyHex = keyAndivHexObj.keyHex
        const ivHex = keyAndivHexObj.ivHex

        var decrypted = CryptoJS.DES.decrypt({
            ciphertext: CryptoJS.enc.Hex.parse(msgHexStr)
        }, keyHex, {
                iv: ivHex,
                mode: CryptoJS.mode.CBC,
                padding: CryptoJS.pad.Pkcs7
            });

        const result = decrypted.toString(CryptoJS.enc.Utf8)
        return result
    }
}
